// PointLightedCube_perFragment.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
  "attribute vec4 a_Position;\n" +
  "attribute vec4 a_Normal;\n" +
  "attribute vec2 a_TexCoord;\n" +
  "uniform mat4 u_MvpMatrix;\n" +
  "uniform mat4 u_ModelMatrix;\n" + // Model matrix
  "uniform mat4 u_NormalMatrix;\n" + // Transformation matrix of the normal
  "varying vec3 v_Normal;\n" +
  "varying vec3 v_Position;\n" +
  "varying vec2 v_TexCoord;\n" +
  "void main() {\n" +
  "  gl_Position = u_MvpMatrix * a_Position;\n" +
  // Calculate the vertex position in the world coordinate
  "  v_Position = vec3(u_ModelMatrix * a_Position);\n" +
  "  v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));\n" +
  "  v_TexCoord = a_TexCoord;\n" +
  "}\n";

// Fragment shader program
var FSHADER_SOURCE =
  "#ifdef GL_ES\n" +
  "precision mediump float;\n" +
  "#endif\n" +
  
  "uniform sampler2D u_Sampler;\n" +
  "uniform vec3 u_LightColor;\n" + // Light color
  "uniform vec3 u_LightPosition;\n" + // Position of the light source
  "uniform vec3 u_EyesPosition;\n" + // Position of the Eyes
  "uniform vec3 u_AmbientLight;\n" + // Ambient light color

  "uniform float u_shininess;\n" +
  "uniform float u_limit;\n" +
  "uniform vec3 u_lightDirection;\n" +

  "varying vec3 v_Normal;\n" +
  "varying vec3 v_Position;\n" +
  "varying vec2 v_TexCoord;\n" +
  
  "void main() {\n" +
  // Normalize the normal because it is interpolated and not 1.0 in length any more
  "  vec3 normal = normalize(v_Normal);\n" +
  // Calculate the light direction and make its length 1.
  "  vec3 lightDirection = normalize(u_LightPosition - v_Position);\n" +
  "  vec3 viewDirection = normalize(u_EyesPosition - v_Position);\n" +
  "  vec3 halfVector = normalize(lightDirection + viewDirection);\n" +
  "  float light = 0.0;\n" +
  "  float specular = 0.0;\n" +
  "  float dotFromDirection = dot(lightDirection,normalize(u_lightDirection));\n" + 

  "  gl_FragColor =  texture2D(u_Sampler,v_TexCoord);\n" +
  // Calculate the final color from diffuse reflection and ambient reflection
  "  vec3 diffuse = u_LightColor * gl_FragColor.rgb;\n" +
  "  vec3 ambient = u_AmbientLight * gl_FragColor.rgb;\n" +

  // The dot product of the light direction and the orientation of a surface (the normal)
  "  if (dotFromDirection >= u_limit) {\n" +
  "     light = max(dot(lightDirection, normal),0.0);\n" +
  "     if(light>0.0){\n" +
  "       specular = pow(dot(normal, halfVector), u_shininess);\n" +
  "     }\n" +
  "  }\n" +
  "  gl_FragColor.rgb = diffuse;\n" +
  "  gl_FragColor.rgb *= light;\n" +
  "  gl_FragColor.rgb += specular;\n" +
  "  gl_FragColor.rgb += ambient;\n"  +
  "}\n";

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById("webgl");

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log("Failed to get the rendering context for WebGL");
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log("Failed to intialize shaders.");
    return;
  }

  //
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log("Failed to set the vertex information");
    return;
  }

  // Set the clear color and enable the depth test
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);

  //加载图片
  var texture = gl.createTexture();
  var u_Sampler = gl.getUniformLocation(gl.program, "u_Sampler");
  var image = new Image();
  image.onload = () => {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
    gl.uniform1i(u_Sampler, 0);
    drawScene();
  };
  image.src = "../../resources/timg.jpg";
  // Get the storage locations of uniform variables
  var u_ModelMatrix = gl.getUniformLocation(gl.program, "u_ModelMatrix");
  var u_MvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");
  var u_NormalMatrix = gl.getUniformLocation(gl.program, "u_NormalMatrix");
  var u_LightColor = gl.getUniformLocation(gl.program, "u_LightColor");
  var u_LightPosition = gl.getUniformLocation(gl.program, "u_LightPosition");
  var u_limit = gl.getUniformLocation(gl.program, "u_limit");
  var u_shininess = gl.getUniformLocation(gl.program, "u_shininess");
  var u_lightDirection = gl.getUniformLocation(gl.program, "u_lightDirection");
  //眼睛位置
  var u_EyesPosition = gl.getUniformLocation(gl.program, "u_EyesPosition");
  var u_AmbientLight = gl.getUniformLocation(gl.program, "u_AmbientLight");
  if (
    !u_ModelMatrix ||
    !u_MvpMatrix ||
    !u_NormalMatrix ||
    !u_LightColor ||
    !u_EyesPosition ||
    !u_LightPosition ||
    !u_AmbientLight
  ) {
    console.log("Failed to get the storage location");
    return;
  }
  var eyesPosition = [0.0, 6.0, 6.0];
  var target = [0, 2, 0];
  var lightPosition = [0.0, 4.0, 0.0];

  var vpMatrix = new Matrix4(); // View projection matrix
  vpMatrix.setPerspective(60, canvas.width / canvas.height, 1, 100);
  vpMatrix.lookAt(...eyesPosition, ...target, 0, 1, 0);

  // Set the light color (white)
  gl.uniform3f(u_LightColor, 1, 1, 1);
  // Set the light direction (in the world coordinate)
  gl.uniform3f(u_LightPosition, ...lightPosition);
  // Set the eyesPosition
  gl.uniform3f(u_EyesPosition, ...eyesPosition);
  // Set the ambient light
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  var currentAngle = 0.0; // Current rotation angle
  var modelMatrix = new Matrix4(); // Model matrix
  var mvpMatrix = new Matrix4(); // Model view projection matrix
  var normalMatrix = new Matrix4(); // Transformation matrix for normals

  function radToDeg(r) {
    return r * 180 / Math.PI;
  }

  function degToRad(d) {
    return d * Math.PI / 180;
  }

  var fieldOfViewRadians = degToRad(60);
  var fRotationRadians = 0;
  var shininess = 150;
  var lightX = 0;
  var lightY = 4;
  var lightZ = 0;
  var eyeX = 0;
  var eyeY = 6;
  var eyeZ = 6;
  var targetX = 0;
  var targetY = 2;
  var targetZ = 0;
  var lightRotationX = 0;
  var lightRotationY = 0;
  var lightDirection = [0, 0, 1];  // this is computed in updateScene
  var limit = degToRad(10);

  gl.uniform1f(u_shininess, shininess);
  // Setup a ui.
  webglLessonsUI.setupSlider("#lightX", {value: lightX, slide: updatelightX, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#lightY", {value: lightY, slide: updatelightY, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#lightZ", {value: lightZ, slide: updatelightZ, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#eyeX", {value: eyeX, slide: updateEyeX, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#eyeY", {value: eyeY, slide: updateEyeY, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#eyeZ", {value: eyeZ, slide: updateEyeZ, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#targetX", {value: targetX, slide: updateTargetX, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#targetY", {value: targetY, slide: updateTargetY, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#targetZ", {value: targetZ, slide: updateTargetZ, min: -2, max: 10, precision: 2, step: 0.001});
  webglLessonsUI.setupSlider("#limit", {value: radToDeg(limit), slide: updateLimit, min: 0, max: 180});

  

  function updatelightX(event, ui) {
    lightX = ui.value;
    drawScene();
  }

  function updatelightY(event, ui) {
    lightY = ui.value;
    drawScene();
  }

  function updatelightZ(event, ui) {
    lightZ = ui.value;
    drawScene();
  }

  function updateEyeX(event, ui) {
    eyeX = ui.value;
    drawScene();
  }

  function updateEyeY(event, ui) {
    eyeY = ui.value;
    drawScene();
  }

  function updateEyeZ(event, ui) {
    eyeZ = ui.value;
    drawScene();
  }

  function updateTargetX(event, ui) {
    targetX = ui.value;
    drawScene();
  }

  function updateTargetY(event, ui) {
    targetY = ui.value;
    drawScene();
  }

  function updateTargetZ(event, ui) {
    targetZ = ui.value;
    drawScene();
  }

  function updateLimit(event, ui) {
    limit = degToRad(ui.value);
    drawScene();
  }



  var drawScene = function () {
    console.error(Math.cos(limit));
    gl.uniform1f(u_limit, Math.cos(limit));
    gl.uniform3f(u_lightDirection, lightX - targetX,lightY - targetY,lightZ - targetZ);
    gl.uniform3f(u_LightPosition, lightX,lightY,lightZ);
    gl.uniform3f(u_EyesPosition, eyeX,eyeY,eyeZ);
    // Calculate the model matrix
    modelMatrix.setRotate(currentAngle, 0, 1, 0); // Rotate around the y-axis
    // Pass the model matrix to u_ModelMatrix
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);

    var vpMatrix = new Matrix4(); // View projection matrix
    vpMatrix.setPerspective(60, canvas.width / canvas.height, 1, 100);
    vpMatrix.lookAt(eyeX,eyeY,eyeZ, targetX,targetY,targetZ, 0, 1, 0);
    // Pass the model view projection matrix to u_MvpMatrix
    mvpMatrix.set(vpMatrix).multiply(modelMatrix);
    gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

    // Pass the matrix to transform the normal based on the model matrix to u_NormalMatrix
    normalMatrix.setInverseOf(modelMatrix);
    normalMatrix.transpose();
    gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

    // Clear color and depth buffer
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    // Draw the cube
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

	  //Draw floor
    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, n);

    //Draw small cube
    modelMatrix = new Matrix4();
    transformMatrix = new Matrix4();
    scaleMatrix = new Matrix4();

    transformMatrix.setTranslate(lightX,lightY,lightZ);
    scaleMatrix.setScale(0.2, 0.2, 0.2);
    modelMatrix = transformMatrix.multiply(scaleMatrix);
    mvpMatrix.set(vpMatrix).multiply(modelMatrix);
    gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

    normalMatrix = new Matrix4();
    normalMatrix.setScale(0, 0, 0);
    gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

  }
  drawScene();
}

function initVertexBuffers(gl) {
  // Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  // Coordinates
  var vertices = new Float32Array([
    2.0,
    2.0,
    2.0,
    -2.0,
    2.0,
    2.0,
    -2.0,
    -2.0,
    2.0,
    2.0,
    -2.0,
    2.0, // v0-v1-v2-v3 front
    2.0,
    2.0,
    2.0,
    2.0,
    -2.0,
    2.0,
    2.0,
    -2.0,
    -2.0,
    2.0,
    2.0,
    -2.0, // v0-v3-v4-v5 right
    2.0,
    2.0,
    2.0,
    2.0,
    2.0,
    -2.0,
    -2.0,
    2.0,
    -2.0,
    -2.0,
    2.0,
    2.0, // v0-v5-v6-v1 up
    -2.0,
    2.0,
    2.0,
    -2.0,
    2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    2.0, // v1-v6-v7-v2 left
    -2.0,
    -2.0,
    -2.0,
    2.0,
    -2.0,
    -2.0,
    2.0,
    -2.0,
    2.0,
    -2.0,
    -2.0,
    2.0, // v7-v4-v3-v2 down
    2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    -2.0,
    2.0,
    -2.0,
    2.0,
    2.0,
    -2.0, // v4-v7-v6-v5 back
    -10.0,
    -6.0,
    -10.0,
    10.0,
    -6.0,
    -10.0,
    10.0,
    -6.0,
    10.0,
    -10.0,
    -6.0,
    10.0, // 地面
  ]);

  // Colors
  var colors = new Float32Array([
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v0-v1-v2-v3 front
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v0-v3-v4-v5 right
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v0-v5-v6-v1 up
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v1-v6-v7-v2 left
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v7-v4-v3-v2 down
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v4-v7-v6-v5 back
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1,
    1, // v4-v7-v6-v5 地面
  ]);

  var verticesTexCoords = new Float32Array([
    0,
    1,
    0,
    0,
    1,
    1,	
    1,
    0,
    0,
    1,
    0,
    0,	//
    1,
    1,
    1,
    0,
    0,
    1,
    0,
    0,
    1,
    1,
    1,
    0,	//
    0,
    1,
    0,
    0,
    1,
    1,
    1,
    0,
    0,
    1,
    0,
    0, //
    1,
    1,
    1,
    0,
    0,
    1,
    0,
    0,
    1,
    1,
    1,
    0, //
    0,
    1,
    0,
    0,
    1,
    1,
    1,
    0, //地面
  ]);

  // Normal
  var normals = new Float32Array([
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0, // v0-v1-v2-v3 front
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0, // v0-v3-v4-v5 right
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0, // v0-v5-v6-v1 up
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0, // v1-v6-v7-v2 left
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0, // v7-v4-v3-v2 down
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0,
    0.0,
    0.0,
    -1.0, // v4-v7-v6-v5 back
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    1.0,
    0.0, // 地面
  ]);

  // Indices of the vertices
  var indices = new Uint8Array([
    0,
    1,
    2,
    0,
    2,
    3, // front
    4,
    5,
    6,
    4,
    6,
    7, // right
    8,
    9,
    10,
    8,
    10,
    11, // up
    12,
    13,
    14,
    12,
    14,
    15, // left
    16,
    17,
    18,
    16,
    18,
    19, // down
    20,
    21,
    22,
    20,
    22,
    23, // back
    24,
    25,
    26,
    24,
    26,
    27, // 地面
  ]);

  // Write the vertex property to buffers (coordinates, colors and normals)
  if (!initArrayBuffer(gl, "a_Position", vertices, 3)) return -1;
  if (!initArrayBuffer(gl, "a_Normal", normals, 3)) return -1;
  if (!initArrayBuffer(gl, "a_TexCoord", verticesTexCoords, 2)) return -1;

  // Unbind the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length - 6;
}

function initArrayBuffer(gl, attribute, data, num) {
  // Create a buffer object
  var buffer = gl.createBuffer();
  if (!buffer) {
    console.log("Failed to create the buffer object");
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log("Failed to get the storage location of " + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, gl.FLOAT, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  gl.enableVertexAttribArray(a_attribute);

  return true;
}

main();
